#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
using namespace std;

//class Person
//{
//public:
//	void Print()
//	{
//		cout << "name:" << _name << endl;
//		cout << "age:" << _age << endl;
//		cout << _tel << endl;
//	}
//protected:
//	string _name = "peter"; // 姓名
//	int _age = 18; // 年龄
//private:
//	// 父类定义本质，不想被子类继承
//	int _tel = 181;
//};
//
//class Student : public Person
//{
//public:
//	void func()
//	{
//		// 子类用不了（不可见）
//		/*cout << _tel << endl;*/ // err
//
//		// 子类可以用
//		cout << _name << endl;
//		cout << _age << endl;
//	}
//protected:
//	int _stuid; // 学号
//};
//
//class Teacher : public Person
//{
//protected:
//	int _jobid; // 工号
//};

//int main()
//{
//	// 构造函数不是同一个
//	Student s;
//	Teacher t;
//
//	// 父类的私有，子类无法直接访问使用
//	// 但是可以调用父类成员函数间接使用
//	s.Print();
//	t.Print();
//
//	//s._name = "张三";
//	//t._name = "张老师";
//
//	Person p;
//	p = s; // 不是类型转换，没有隐式类型转换，没有中间变量
//	
//	// 赋值兼容转换
//	Person* ptr = &s;
//	// 中间没有临时变量
//	Person& ref = s;
//
//	ptr->_name += 'x';
//	ref._name += 'y';
//	s._name += 'z';
//
//	// 可以子给父，无法父给子
//
//	return 0;
//}

//class Person
//{
//protected:
//	string _name = "小李子"; // 姓名
//	int _num = 111; // 身份证号
//};
//class Student : public Person
//{
//public:
//	void Print()
//	{
//		// 先子类，后父类，最后全局
//		// 访问父类要指定作用域
//		cout << " 姓名:" << _name << endl;
//		cout << " 身份证号:" << Person::_num << endl;
//		cout << " 学号:" << _num << endl;
//	}
//protected:
//	int _num = 999; // 学号
//};
//void Test()
//{
//	Student s1;
//	s1.Print();
//};
//
//int main()
//{
//	Test();
//
//	return 0;
//}

//class A
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//};
//
//class B : public A
//{
//public:
//	void fun(int i)
//	{
//		// B中的fun和A中的fun不是构成重载，因为不是在同一作用域
//		// B中的fun和A中的fun构成隐藏，成员函数满足函数名相同就构成隐藏
//		// 在继承体系里面最好不要定义同名的成员
//		A::fun();
//		cout << "func(int i)->" << i << endl;
//	}
//};
//
//void Test()
//{
//	B b;
//	b.fun(10);
//	b.A::fun(); 
//};
//
//int main()
//{
//	Test();
//
//	return 0;
//}

//// 默认构造函数
//class Person
//{
//public:
//	Person(const char* name = "peter")
//		: _name(name)
//	{
//		cout << "Person()" << endl;
//	}
//
//	Person(const Person& p)
//		: _name(p._name)
//	{
//		cout << "Person(const Person& p)" << endl;
//	}
//
//	Person& operator=(const Person& p)
//	{
//		cout << "Person operator=(const Person& p)" << endl;
//		if (this != &p)
//			_name = p._name;
//
//		return *this;
//	}
//
//	~Person()
//	{
//		cout << "~Person()" << endl;
//	}
//protected:
//	string _name; // 姓名
//};
//
//class Student : public Person
//{
//public:
//	Student(const char* name, int x, const char* address)
//		// 不可以直接初始化父类成员
//		// 必须得去复用
//		// “家里面公司就缺个CEO，你还要自己单开证明自己？”
//		:Person(name)
//		,_x(x)
//		,_address(address)
//	{}
//
//	Student(const Student& st)
//		:Person(st)
//		,_x(st._x)
//		,_address(st._address)
//	{}
//
//	Student& operator=(const Student& st)
//	{
//		if (this != &st)
//		{
//			// 有时候还是不可避免要用重名变量
//			Person::operator=(st);
//			_x = st._x;
//			_address = st._address;
//		}
//		return *this;
//	}
//
//	// 由于多态，析构函数的名字会被统一处理成destructor()
//	// 所以析构函数会构成隐藏关系
//	~Student()
//	{
//		// 析构函数会构成隐藏，所以这里要指定类域
//		// 在这里其他几个都要显式调用，就析构不用
//		// 构造的时候先父后子，析构的时候先子后父
//		// 子类的构造有可能会调用父类，所以先父后子
//		// 子类的析构可能会访问父类成员，所以得先子后父
//		// 所以父类析构不是自己显式调用的
//		// 子类析构结束后，自动调用
//		// 构造显式调用能保证先父后子
//		// 因为初始化列表里变量的出现顺序并不代表初始化的顺序
//		// Person::~Person();
//		cout << "~Student()" << endl;
//	}
//protected:
//	int _x = 1;
//	string _address = "";
//};

//	子类默认生成的构造
// 父类成员（整体）-- 默认构造
// 子类自己的内置成员 -- 一般不处理
// 子类自己的自定义成员 -- 默认构造

//	子类默认生成的拷贝构造 赋值重载跟拷贝构造类似
// 父类成员（整体）-- 调用他的拷贝构造
// 子类自己的内置成员 -- 值拷贝
// 子类自己的自定义成员 -- 调用他的拷贝构造
// 一般就不需要自己写了，除了子类成员涉及深拷贝

// 子类默认生成的析构
// 父类成员（整体）-- 默认析构
// 子类自己的内置成员 -- 不处理
// 子类自己的自定义成员 -- 默认析构

//int main()
//{
//	Student s("张三", 1, "龙岩");
//
//	return 0;
//}

//class A
//{
//public:
//	int _a = 1;
//protected:
//	int _b = 2;
//private:
//	int _c = 3;
//};
//
//class B : public A
//{
//public:
//	B()
//	{
//		cout << _a << endl;
//		cout << _b << endl;
//		/*cout << A::_c << endl;*/
//	}
//};
//
//int main()
//{
//	B b;
//	b._a = 2;
//
//	return 0;
//}

//class Student;
//
//class Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名
//};
//
//class Student : public Person
//{
//protected:
//	int _stuNum; // 学号
//};
//
//void Display(const Person& p, const Student& s)
//{
//	cout << p._name << endl;
//	// 我的附庸的附庸不是我的附庸
//	/*cout << s._stuNum << endl;*/
//}
//
//int main()
//{
//	Person p;
//	Student s;
//	Display(p, s);
//}

//class Person
//{
//public:
//	Person() { ++_count; }
//protected:
//	string _name; // 姓名
//public:
//	static int _count; // 统计人的个数。
//};
//int Person::_count = 0;
//
//class Student : public Person
//{
//protected:
//	int _stuNum; // 学号
//};
//
//class Graduate : public Student
//{
//protected:
// string _seminarCourse; // 研究科目
//};
//
//void TestPerson()
//{
//	Student s1;
//	Student s2;
//	Student s3;
//	Graduate s4;
//
//	cout << " 人数 :" << Person::_count << endl;
//
//	Student::_count = 0;
//
//	cout << " 人数 :" << Person::_count << endl;
//}
//
//int main()
//{
//	TestPerson();
//
//	return 0;
//}

//class Person
//{
//public:
//	string _name; // 姓名
//};
//
//class Student : public Person
//{
//protected:
//	int _num; //学号
//};
//
//class Teacher : public Person
//{
//protected:
//	int _id; // 职工编号
//};
//
//class Assistant : public Student, public Teacher
//{
//protected:
//	string _majorCourse; // 主修课程
//};
//
// 实践中，不建议搞菱形继承
//void Test()
//{
//	// 这样会有二义性无法明确知道访问的是哪一个
//	Assistant a;
//	a._name = "peter";
//	// 需要显示指定访问哪个父类的成员可以解决二义性问题，但是数据冗余问题无法解决
//	a.Student::_name = "xxx";
//	a.Teacher::_name = "yyy";
//}
//
//int main()
//{
//	Test();
//
//	return 0;
//}

//class Person
//{
//public:
//	int _a;
//};
//
//class Student : Person
//{
//public:
//	int _b;
//};
//
//int main()
//{
//	Student s;
//	Person p;
//
//	return 0;
//}

class Person
{
public:
	string _name; // 姓名
};

class Student : virtual public Person
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

// Assistant有两份Person类，存在数据冗余和二义性问题
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	Assistant a;
	// a._name = "peter"; // 这样会有二义性无法明确知道访问的是哪一个
	// 需要显示指定访问哪个父类的成员可以解决二义性问题，但是数据冗余问题无法解决
	a._name = "peter";
}

